#!/usr/bin/env/python

import socket
import select
import gobject
import urllib
import gtk
import threading

import user
import chat

#gtk.gdk.threads_init()
gobject.threads_init()

class msn(threading.Thread):

	def __init__(self, email, password, gui):
		threading.Thread.__init__(self)
		self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
		self.email = email
		self.password = password
		self.gui = gui
		self.online_users = {}
		self.lst_total = 0
		self.syn_total = 0
		self.tr_id = 0
		self.qng = 0
		self.msg = 0


	def _read_line(self):
		char = self.socket.recv(1)		
		prev_char = ''
		received_line = ''
		while char != "\n":
			received_line = received_line + char
			prev_char = char
			char = self.socket.recv(1)

		if char == "\n" and prev_char == "\r":
			received_line = received_line + char
			return received_line
		

	def _parse_sync(self, cmd, params=None):
		if params:
			command = cmd + ' ' + str(self.tr_id) + ' ' + params + '\r\n'
		else:
			command = cmd + ' ' + str(self.tr_id) + '\r\n'

		if self.socket.send(command):
			print ">> ", repr(command)
			self.tr_id += 1
			server_answer = self._read_line()
			if server_answer:
				print "<< ", repr(server_answer)
				return server_answer

	def run(self):
		print "Thread started"
		self._login()


	def _login(self):
		if self._establish():
			gobject.timeout_add(10, self._watch_socket)

	def _watch_socket(self):
		socket_descriptor = self.socket.fileno()
		r,w,e = select.select([socket_descriptor], [], [], 0)
		if len(r) > 0:
			buf = self._read_line()
			self._process_incoming(buf)

		return True

	def _establish(self):
		self.socket.connect(("messenger.hotmail.com", 1863))
		self._parse_sync("VER", "MSNP11 MSNP10")
		self._parse_sync("CVR", "0x0409 linux 7.1 i386 momsn 0.8.1 msmsgs " +self.email+ "")
		new_server = self._parse_sync("USR", "TWN I " +self.email+ "").split(" ")[3].split(":")[0]
		self.socket.close()
		self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
		self.socket.connect((new_server, 1863))
		self._parse_sync("VER", "MSNP11 MSNP10")
		self._parse_sync("CVR", "0x0409 linux 7.1 i386 momsn 0.8.1 msmsgs " +self.email+ "")
		auth_key = self._parse_sync("USR", "TWN I " +self.email+ "").split(" ")
		if auth_key[0] == "USR":
			auth_key = self.passport_auth(auth_key[4])
			ok = self._parse_sync("USR", "TWN S " +auth_key+ "").split(" ")[2]
			if ok == "OK":
				return True
		else:
			print "Couldnt connect please try again later.."
			return False
		

	def _process_incoming(self,buf):
		"""Trigger is gobject.IO_IN"""
		command = buf.split(" ")[0]

		if command == "MSG":
			print "GOT msg ", command
			payload = int(buf.split(" ")[3][:-2])
			msg = self.socket.recv(payload)


			if not self.msg:
				self.syn_total = int(self._parse_sync("SYN", "0 0").split(" ")[4])
				self.msg = 1

		elif command == "PRP":
			prp_type = buf.split(" ")[1]
			if prp_type == "MFN":
				my_nick = buf.split(" ")[2][:-2]
				my_instance = user.Instance(self.email,my_nick,"")
				self.gui._contact_window(my_instance,self)
				
		elif command == "LST":
			self.lst_total += 1
			user_email = buf.split(" ")[1].split("N=")[1] #returns the email
			gtk.gdk.threads_enter()
			self.gui._add_user(email=user_email)
			gtk.gdk.threads_leave()
			if self.lst_total == self.syn_total:
				print "Got List.., changing state"
				self.socket.send("CHG "+str(self.tr_id)+" NLN 0\r\n")
				self.socket.send("PNG\r\n")

		elif command == "UBX":
			payload = int(buf.split(" ")[2][:-2])
			if payload > 0:
				ubx = self.socket.recv(payload)
			#process PM here and song

		elif command == "NLN":
			email = buf.split(" ")[2]
			nick = urllib.unquote(buf.split(" ")[3])
			state = buf.split(" ")[1]
			if email not in self.online_users.keys():
				userInstance = user.Instance(email, nick, state)
				self.online_users[email] = userInstance
				print "Adding userInstance.."
				self.gui._add_user(userInstance)
			else:
				self.online_users[email].nick = nick
				self.online_users[email].state = state#Assign icons to Items

		elif command == "ILN":
			chunks = buf.split(" ")
			print chunks
			email = chunks[3]
			nick = urllib.unquote(chunks[4])
			state = chunks[2]
			msn_object = ''
			userInstance = user.Instance(email, nick, state)
			if len(chunks) == 7:
				msn_object = chunks[6]
				userInstance.msn_object = msn_object

			self.online_users[email] = userInstance
			self.gui._add_user(userInstance)

		elif command == "FLN":
			email = buf.split(" ")[1][:-2] #remove \r\n from the email
			del self.online_users[email]
			self.gui._rem_user(email)
			#process delete from gui here too

		elif command == "QNG":
			if not self.qng:
				if len(self.online_users) > 0:
					print self.online_users
					self.qng = 1
				else:
					self.socket.send("PNG\r\n")


		elif command == "RNG":
			"Got Ring"
			xfr = buf.split(" ")[2].split(":")[0]
			inviter = buf.split(" ")[5]

			#sess_details = [auth Key, session ID]
			sess_details = [xfr, buf.split(" ")[4], buf.split(" ")[1]] 

			user_instance = self.online_users[inviter]
			user_chat = chat.Session(self.email, sess_details, user_instance)
			user_chat._show_gui()
			user_chat._invited()
			


		elif command == "CHL":
			from challenge import doChallenge
			qry = doChallenge(buf.split(" ")[2][:-2])
			qry = "QRY "+str(self.tr_id)+" PROD0090YUAUV{2B 32\r\n"+qry
			self.socket.send(qry)
			print repr(self._read_line())

		else:
			print buf

		return True

	def _change_nick(self, new_nick):
		encoded_nick = urllib.quote(new_nick)
		self._parse_sync("PRP", "MFN "+encoded_nick)


	def _change_pm(self, new_pm):
		payload = "<Data><PSM>"+new_pm+"</PSM><CurrentMedia></CurrentMedia></Data>"
		self.socket.send("UUX "+str(self.tr_id)+" "+str(len(payload))+"\r\n"+payload)

	def _add_contact(self, user_email):
		self._parse_sync("ADC", "FL N="+user_email+" F="+user_email)

	def passport_auth(self, hash):
		"""Logins into passport and obtains an ID used for
		authorization; it's a helper function for login"""
		import httplib

		# initial connection
		nexus = urllib.urlopen('https://nexus.passport.com/rdr/pprdr.asp')
		h = nexus.headers
		purl = h['PassportURLs']

		# parse the info
		d = {}
		for i in purl.split(','):
		        key, val = i.split('=', 1)
			d[key] = val

		# get the login server
		login_server = 'https://' + d['DALogin']
		login_host = d['DALogin'].split('/')[0]

		# build the authentication headers
		ahead =  'Passport1.4 OrgVerb=GET'
		ahead += ',OrgURL=http%3A%2F%2Fmessenger%2Emsn%2Ecom'
		ahead += ',sign-in=' + urllib.quote(self.email)
		ahead += ',pwd=' + urllib.quote(self.password)
		ahead += ',lc=1033,id=507,tw=40,fs=1,'
		ahead += 'ru=http%3A%2F%2Fmessenger%2Emsn%2Ecom,ct=0,'
		ahead += 'kpp=1,kv=5,ver=2.1.0173.1,tpf=' + hash
		headers = { 'Authorization': ahead }

		# connect to the given server
		ls = httplib.HTTPSConnection(login_host)

		# make the request
		ls.request('GET', login_server, '', headers)
		resp = ls.getresponse()

		# loop if we get redirects until we get a definitive answer
		while resp.status == 302:
			login_server = resp.getheader('Location')
			login_host = login_server.split('/')[2]
			ls = httplib.HTTPSConnection(login_host)
			headers = { 'Authorization': ahead }
			ls.request('GET', login_server, '', headers)
			resp = ls.getresponse()

		# now we have a definitive answer, if it's not 200 (success)
		# just raise AuthError
		if resp.status != 200:
			# for now we raise 911, which means authentication
			# failed; but maybe we can get more detailed
			# information
			raise 'AuthError', [911, 'SSL Auth failed']

		# and parse the headers to get the passport id
		try:
			ainfo = resp.getheader('Authentication-Info')
		except:
			ainfo = resp.getheader('WWW-Authenticate')

		d = {}
		for i in ainfo.split(','):
			key, val = i.split('=', 1)
			d[key] = val

		passportid = d['from-PP']
		passportid = passportid[1:-1]		# remove the "'"
		return passportid


class gui:
	
	def __init__(self):
		self.window = gtk.Window()
		self.window.connect("destroy", self._quit)
		self.main_box = gtk.VBox(False,0) # adds the sub boxes, login window has a sub box and 						conact window has a separate one
		self.main_box.show()
		self.window.add(self.main_box)
		self.offline_iters = {}
		self.online_iters = {}


	def _quit(self, window):
		gtk.main_quit()

	def _login_window(self):
		self.login_box = gtk.VBox(False, 0)
		self.login_box.show()

		main_hb = gtk.HBox(False, 25)  # Dp    |    login details
		main_hb.show()

		user_dp = gtk.Image()
		#Assign icons to Items
		MyTheme = gtk.icon_theme_get_default()
		MyIconPixbuf = MyTheme.load_icon("stock_smiley-3", 36, gtk.ICON_LOOKUP_FORCE_SVG)

		user_dp.set_from_pixbuf(MyIconPixbuf)
		user_dp.set_size_request(60,60)
		user_dp.show()

		details_vb = gtk.VBox(False, 0)  #  user entry
					 #  pass entry
		details_vb.show()
		bottom_hb = gtk.HBox()	 # remember boxes   | Login button
		bottom_hb.show()
		remember_vb = gtk.VBox()
		remember_vb.show()	# remember me
					# remember pass
		
		self.user_email = gtk.Entry()
		self.user_email.set_text("someone@somewhere.com")
		self.user_email.show()

		self.user_pass = gtk.Entry()
		#self.user_pass.set_text("Set Password here")
		self.user_pass.set_visibility(False)
		self.user_pass.show()

		self.remember_me = gtk.CheckButton("Remember Me")
		self.remember_me.show()
		self.remember_pass = gtk.CheckButton("Remember Password")
		self.remember_pass.show()

		remember_vb.pack_start(self.remember_me)
		remember_vb.pack_start(self.remember_pass)

		login = gtk.Button("Login")
		login.connect("clicked", self._login)
		login.show()

		bottom_hb.pack_start(remember_vb, False, False, 5)
		bottom_hb.pack_start(login, True, True)

		details_vb.pack_start(self.user_email)
		details_vb.pack_start(self.user_pass)
		
		details_vb.pack_end(bottom_hb)
		main_hb.pack_start(user_dp, False, False, 10) #Dp goes here
		main_hb.pack_start(details_vb, False, False, 10)
		self.login_box.pack_start(main_hb)
		self.main_box.pack_start(self.login_box)
		self.window.set_size_request(370,200)
		self.window.show()

		gtk.main()

	def _login(self, button):
		email = self.user_email.get_text()
		password = self.user_pass.get_text()
		m = msn(email, password, self)
		m.start()
		self.login_box.set_sensitive(False)


##----------------------------------------------------------------------------------------------
##				Contact Window starts Here
##--------------------------------------------------------------------------------------------

	def _contact_window(self, my_instance, main_server):
		self.login_box.hide()
		self.box = gtk.VBox(False, 0)
		self.contact_box = gtk.VBox(False, 0)
		self.contact_box.show()

		self.users_list = gtk.TreeStore(gtk.gdk.Pixbuf,gobject.TYPE_STRING,gobject.TYPE_STRING)

		self.users_tree = gtk.TreeView(self.users_list)	
		self.users_tree.connect("row-activated", self._start_chat)	
		self.me = my_instance
		self.ns = main_server

		top_box = gtk.HBox(False, 0)

		add_box = gtk.VBox(False, 1)
		add_btn = gtk.Button("Add")
		add_btn.connect("clicked", self._new_contact)
		add_pb =  self.users_tree.render_icon(gtk.STOCK_ADD, gtk.ICON_SIZE_BUTTON)
		add_img = gtk.Image()
		add_img.set_from_pixbuf(add_pb)
		add_btn.set_relief(gtk.RELIEF_NONE)
		add_box.pack_start(add_img, False, False, 0)
		add_box.pack_start(add_btn, False, False, 0)


		send_box = gtk.VBox(False, 1)
		send_pb =  self.users_tree.render_icon(gtk.STOCK_GO_FORWARD, gtk.ICON_SIZE_BUTTON)
		send_btn = gtk.Button("Send")
		send_btn.set_relief(gtk.RELIEF_NONE)
		send_img = gtk.Image()
		send_img.set_from_pixbuf(send_pb)
		send_btn.set_relief(gtk.RELIEF_NONE)
		send_box.pack_start(send_img, False, False, 0)
		send_box.pack_start(send_btn, False, False, 0)


		mail_box = gtk.VBox(False, 1)
		mail_pb =  self.users_tree.render_icon(gtk.STOCK_HOME, gtk.ICON_SIZE_BUTTON)
		mail_btn = gtk.Button("Mail")
		mail_btn.set_relief(gtk.RELIEF_NONE)
		mail_img = gtk.Image()
		mail_img.set_from_pixbuf(mail_pb)
		mail_btn.set_relief(gtk.RELIEF_NONE)
		mail_box.pack_start(mail_img, False, False, 0)
		mail_box.pack_start(mail_btn, False, False, 0)

		top_box.pack_start(add_box, False, False, 10)
		top_box.pack_start(send_box, False, False, 10)
		top_box.pack_start(mail_box, False, False, 10)
		top_box.show()
		
		user_box = gtk.HBox(False, 5)
		dp_box = gtk.VBox(False, 0)
		nick_box = gtk.VBox(False, 0)

		user_dp = gtk.Image()
		MyTheme = gtk.icon_theme_get_default()
		MyIconPixbuf = MyTheme.load_icon("stock_smiley-3", 36, gtk.ICON_LOOKUP_FORCE_SVG)
		user_dp.set_from_pixbuf(MyIconPixbuf)
		user_dp.set_size_request(30,30)
		dp_box.pack_start(user_dp, False, False, 5)

		self.nick_btn = gtk.Button(self.me.nick)
		self.nick_btn.set_relief(gtk.RELIEF_NONE)
		self.nick_btn.connect("clicked", self._new_nick)
		self.nick_entry = gtk.Entry()
		self.nick_entry.set_text(self.me.nick)
		self.nick_entry.set_no_show_all(True)
		self.nick_entry.connect("activate", self._change_nick)
		self.pm_btn = gtk.Button("(Type a personal message)")
		self.pm_btn.set_relief(gtk.RELIEF_NONE)
		self.pm_btn.connect("clicked", self._new_pm)
		self.pm_entry = gtk.Entry()
		self.pm_entry.set_text("(Type a personal message)")
		self.pm_entry.set_no_show_all(True)
		self.pm_entry.connect("activate", self._change_pm)
		nick_box.pack_start(self.nick_btn, False, True, 0)
		nick_box.pack_start(self.nick_entry, False, True, 0)
		nick_box.pack_start(self.pm_btn, False, True, 0)
		nick_box.pack_start(self.pm_entry, False, True, 0)

		user_box.pack_start(dp_box, False, False, 3)
		user_box.pack_start(nick_box, False, False, 0)
		
		col = gtk.TreeViewColumn()
		render_pixbuf = gtk.CellRendererPixbuf()
		col.pack_start(render_pixbuf, expand=False)
		col.add_attribute(render_pixbuf, 'pixbuf', 0)
		render_text = gtk.CellRendererText()
		col.pack_start(render_text, expand=True)
		col.add_attribute(render_text, 'text', 1)
		col.pack_start(render_text, expand=False)
		col.add_attribute(render_text, 'text', 2)
		self.users_tree.append_column(col)

		column = gtk.TreeViewColumn('email', gtk.CellRendererText(),
		text=1)
		column.set_visible(False)
		self.users_tree.append_column(column)

		self.onlineg_img = self.users_tree.render_icon(gtk.STOCK_YES, gtk.ICON_SIZE_SMALL_TOOLBAR)

		self.offlineg_img = self.users_tree.render_icon(gtk.STOCK_NO, gtk.ICON_SIZE_SMALL_TOOLBAR)


		self.online_img = self.users_tree.render_icon(gtk.STOCK_YES, gtk.ICON_SIZE_MENU)

		self.offline_img = self.users_tree.render_icon(gtk.STOCK_NO, gtk.ICON_SIZE_MENU)
		

		self.online_iter = self.users_list.append(None, (self.onlineg_img, 'Online', 'online'))
		self.offline_iter = self.users_list.append(None, (self.offlineg_img, 'Not Online', 'offline'))

		
		self.users_tree.show()
	
		self.cw_statusbar = gtk.Statusbar()
		self.context_id = self.cw_statusbar.get_context_id("Statusbar")
		self.cw_statusbar.push(self.context_id, "Logged In")

		self.box.pack_start(top_box, False, True, 0)
		self.box.pack_start(user_box, False, True, 0)
		self.box.pack_start(self.users_tree, True, True, 0)
		self.box.pack_start(self.cw_statusbar, False, True, 0)

		self.box.show_all()
		self.contact_box.pack_start(self.box)
		self.main_box.pack_start(self.contact_box)
		self.window.set_size_request(370,500)

		

	def combo_text(self, combobox):
		model = combobox.get_model()
		active = combobox.get_active()
		if active < 0:
			return None
		return model[active][0]

	def _add_user(self, userInstance=None, email=None):
		"""If the user instance is set, that means the user signed """
		if userInstance:
			print "Adding User Instance.."
			nick = userInstance.nick
			email = userInstance.email
			user_iter = self.users_list.append(self.online_iter, (self.online_img, nick, email))
			self.users_list.remove(self.offline_iters[email])
			self.online_iters[email] = user_iter
			if email in self.offline_iters.keys():
				del self.offline_iters[email]
			self.cw_statusbar.push(self.context_id, ""+email+": Came Online")
	
		else:
			"""If the userinstance isnt, we just get an email from the lst and add it to
			the offline group"""
			user_iter = self.users_list.append(self.offline_iter, (self.offline_img, email, email))
			self.offline_iters[email] = user_iter


	def _rem_user(self, email):
		self.users_list.remove(self.online_iters[email])
		user_iter = self.users_list.append(self.offline_iter, (self.offline_img, email, email))
		self.offline_iters[email] = user_iter
		if email in self.online_iters.keys():
			del self.online_iters[email]
		self.cw_statusbar.push(self.context_id, ""+email+": Went Offline")


	def _start_chat(self,treeview, path, view_column):
		t_iter = self.users_list.get_iter(path)
		email = self.users_list.get_value(t_iter,2)
		user_instance = self.ns.online_users[email]
		session = self.ns._parse_sync("XFR", "SB")
		xfr = session.split(" ")[3].split(":")[0]
		session_auth = session.split(" ")[5][:-2]
		sess_details = [xfr, session_auth]
		user_chat = chat.Session(self.me.email,sess_details, user_instance, 1)
		user_chat._show_gui()
		user_chat._inviter()

	def _new_nick(self, button):
		self.nick_btn.hide()
		self.nick_entry.show()

	def _change_nick(self, entry):
		new_nick = self.nick_entry.get_text()
		self.ns._change_nick(new_nick)
		self.nick_entry.hide()
		self.nick_btn.set_label(new_nick)
		self.nick_btn.show()

	def _new_pm(self, button):
		self.pm_btn.hide()
		self.pm_entry.show()

	def _change_pm(self, entry):
		new_pm = self.pm_entry.get_text()
		self.ns._change_pm(new_pm)
		self.pm_entry.hide()
		self.pm_btn.set_label(new_pm)
		self.pm_btn.show()

	def _new_contact(self, button):
		self.add_user_dialog = gtk.Dialog("Add Contact", self.window, 0, buttons=None)
		self.add_user_email = gtk.Entry()
		self.add_user_email.set_text("contacts email")
		self.add_user_email.show()
		add_user = gtk.Button("Add")
		add_user.connect("clicked", self._add_contact)
		add_user.show()

		self.add_user_dialog.vbox.pack_start(self.add_user_email, False, False, 0)
		self.add_user_dialog.vbox.pack_start(add_user, False, False, 0)
		self.add_user_dialog.show()

	def _add_contact(self, button):
		user_email = self.add_user_email.get_text()
		self.ns._add_contact(user_email)
		self.add_user_dialog.destroy()
		
		
def main():
	g = gui()
	g._login_window()


if __name__ == "__main__":
	main()
